﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Krile.Forms.MainForm.Controls.Surface
{
    public class SurfaceRefreshManager
    {
        public delegate void UpdateMethod(bool updatecompz);
        private UpdateMethod updmethod;
        /// <summary>
        /// 更新を実行します。
        /// </summary>
        private void DoUpdate()
        {
            updmethod.Invoke(reqUpdateComposition);
            reqUpdateComposition = false;
        }

        public SurfaceRefreshManager(UpdateMethod updateMethod)
        {
            updmethod = updateMethod;
        }

        bool reqUpdateComposition = false;

        /// <summary>
        /// サーフェイスを再描画します。
        /// </summary>
        /// <param name="updatecompz">再度コンポジションを実行します。</param>
        public void RefreshSurface(bool updatecompz)
        {
            RefreshSurface(updatecompz, false);
        }

        /// <summary>
        /// サーフェイスを再描画します。
        /// </summary>
        /// <param name="updatecompz">再度コンポジションを実行します。</param>
        /// <param name="lowpriority">低い優先度で処理を行います。(100～1000ms程度更新の遅れが出ることがあります。)</param>
        public void RefreshSurface(bool updatecompz, bool lowpriority)
        {
            if (updatecompz)
                reqUpdateComposition = true;
            if (isSuspending)
            {
                reqUpdateInRestore = true;

                return;
            }
            if (lowpriority)
            {
                var lpu = new Action(DoLowPriorityUpdate);
                lpu.BeginInvoke((iar) => ((Action)iar.AsyncState).EndInvoke(iar), lpu);
            }
            else
                this.DoUpdate();
        }

        const int LowPriorityUpdateIntervalMs = 500;
        object lpulocker = new object();
        bool lowPriorityUpdating = false;
        /// <summary>
        /// 低プライオリティ時の更新処理を行います。
        /// </summary>
        private void DoLowPriorityUpdate()
        {
            lock (lpulocker)
            {
                if (lowPriorityUpdating)
                    return;
                lowPriorityUpdating = true;
            }
            try
            {
                Thread.Sleep(LowPriorityUpdateIntervalMs);
                DoUpdate();
            }
            finally
            {
                lowPriorityUpdating = false;
            }
        }

        /// <summary>
        /// Resize時用アップデータ。別要求で描画を更新します
        /// </summary>
        public void RefreshSurfaceDelay()
        {
            reqUpdateComposition = true;
            var du = new Action(DoDelayedUpdate);
            du.BeginInvoke((iar) => ((Action)iar.AsyncState).EndInvoke(iar), du);
        }

        object dulocker = new object();
        bool delayedUpdating = false;
        private void DoDelayedUpdate()
        {
            lock (dulocker)
            {
                if (delayedUpdating)
                    return;
                delayedUpdating = true;
            }
            try
            {
                Thread.Sleep(10);
                DoUpdate();
            }
            finally
            {
                delayedUpdating = false;
            }
        }

        /// <summary>
        /// サーフェイス再描画を一時停止します。
        /// </summary>
        public void Suspend()
        {
            isSuspending = true;
            reqUpdateInRestore = false;
        }

        bool reqUpdateInRestore = false;
        /// <summary>
        /// サーフェイス再描画を再開します。
        /// </summary>
        /// <param name="enforceRefresh">リクエストされていたかどうかにかかわらず、必ず再描画を行います。</param>
        public void Restore(bool enforceRefresh)
        {
            isSuspending = false;
            if (enforceRefresh || reqUpdateInRestore)
                this.DoUpdate();
        }

        bool isSuspending = false;
        /// <summary>
        /// 現在サスペンド状態であるかを取得します。
        /// </summary>
        public bool IsSuspending { get { return isSuspending; } }

        /// <summary>
        /// 次回更新でコンポジションの更新が必要なことを通知します。
        /// </summary>
        public void SetUpdateComposition()
        {
            reqUpdateComposition = true;
        }
    }
}
